/* === S T A R T =========================================================== */
/* === H E A D E R S ======================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <gtkmm/dialog.h>
#include <gtkmm/entry.h>

#include <synfig/valuenode_dynamiclist.h>
#include <synfigapp/action_system.h>
#include <synfig/valuenode_bline.h>

#include "state_bone.h"
#include "canvasview.h"
#include "workarea.h"
#include "app.h"

#include <synfigapp/action.h>
#include "event_mouse.h"
#include "event_layerclick.h"
#include "toolbox.h"
#include "dialog_tooloptions.h"
#include <gtkmm/optionmenu.h>
#include "duck.h"
#include "widget_enum.h"
#include <synfigapp/main.h>

#include "general.h"

#endif

// libgda stuff
#include <libgda/libgda.h>


/* === U S I N G =========================================================== */

using namespace std;
using namespace etl;
using namespace synfig;
using namespace studio;

/* === M A C R O S ========================================================= */

enum BoneFalloff
{
	CIRCLE_SQUARED	=0,
	CIRCLE_INTERPOLATION_LINEAR	=1,
	CIRCLE_COSINE	=2,
	CIRCLE_SIGMOND	=3,
	CIRCLE_SQRT		=4,
	CIRCLE_NUM_FALLOFF
};

/* === G L O B A L S ======================================================= */

ARTStateBone studio::state_bone;

/* === M E T H O D S ======================================================= */

// ###########################################################################
// ### State Bone Context
// ###########################################################################

ARTStateBoneContext::ARTStateBoneContext(CanvasView* canvas_view)
	: canvas_view_(canvas_view)
	, is_working(*canvas_view)
	, duckmatic_push(get_work_area())
	, prev_workarea_layer_status_(get_work_area()->get_allow_layer_clicks())
	, settings(synfigapp::Main::get_selected_input_device()->settings())
	, m_rEntryId() // value lower upper step page
	/*
	, adj_feather(					0,    0,    1, 0.01, 0.1)
	, adj_number_of_bline_points(		0,    2,  120, 1   , 1  )
	, adj_bline_point_angle_offset(	0, -360,  360, 0.1 , 1  )
	, spin_feather(adj_feather,0.1,3)
	, spin_number_of_bline_points(adj_number_of_bline_points,1,0)
	, spin_bline_point_angle_offset(adj_bline_point_angle_offset,1,1)
	, checkbutton_invert(_("Invert"))
	, checkbutton_layer_bone(_("Create Bone Layer"))
	, checkbutton_layer_region(_("Create Region BLine"))
	, checkbutton_layer_outline(_("Create Outline BLine"))
	, checkbutton_layer_curve_gradient(_("Create Curve Gradient BLine"))
	, checkbutton_layer_plant(_("Create Plant BLine"))
	, checkbutton_layer_link_origins(_("Link BLine Origins"))
	*/
{
	egress_on_selection_change = true;
	
	// IAKKO: 'attacca' la tabella delle opzioni nell'apposita finestra
	options_table.attach(*manage(new Gtk::Label(_("Bone Tool"))),	0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
	options_table.attach(m_rEntryId,									0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
	
	// IAKKO: Caricamento delle impostazioni
	loadSettings();
	
	// IAKKO: Mostra la tabella delle opzioni (preventivamente inserita nel controllore delle tabelle)
	options_table.show_all();

	// IAKKO: Non e' ben chiaro cosa faccia questo metodo
	refresh_tool_options();
	App::dialog_tool_options->present();

	// Turn off layer clicking
	get_work_area()->set_allow_layer_clicks(false);

	// clear out the ducks
	get_work_area()->clear_ducks();

	// Refresh the work area
	get_work_area()->queue_draw();

	get_canvas_view()->work_area->set_cursor(Gdk::CROSSHAIR);

	App::toolbox->refresh();
	
	m_rPoints.clear();
	m_rDucks.clear();
	
	dbg("ARTStateBoneContext::ARTStateBoneContext()");
	
	// IAKKO: dummy method giusto per provare la libgda...
	gda_init ("TestGDA", "0.1", 0, 0);
	doGDA();
}

ARTStateBoneContext::~ARTStateBoneContext()
{
	doBone();

	saveSettings();

	// Restore layer clicking
	get_work_area()->set_allow_layer_clicks(prev_workarea_layer_status_);
	get_canvas_view()->work_area->reset_cursor();

	App::dialog_tool_options->clear();

	// Refresh the work area
	get_work_area()->queue_draw();

	get_canvas_view()->queue_rebuild_ducks();

	App::toolbox->refresh();

	dbg("ARTStateBoneContext::~ARTStateBoneContext()");
}


void ARTStateBoneContext::loadSettings()
{
	// IAKKO: Questo metodo carica le informazioni
	String value;

	//parse the arguments yargh!
	if(settings.get_value("bone.id", value))
		setId(value);
	else
		setId("Bone");
	
	dbg("loadSettings() %s", getId().c_str());
}

void ARTStateBoneContext::saveSettings()
{
	// IAKKO: Questo metodo salva le informazioni
	settings.set_value("bone.id", getId());
}

void
ARTStateBoneContext::reset()
{
	// Aggiorna i duck nel canvas
	refresh_ducks();
}

void
ARTStateBoneContext::increment_id()
{
	// IAKKO: Questo metodo calcola il numero di layer generati da questo strumento e inserisce questa
	//        informazione all'interno del nome del layer stesso cosi' da poterli distinguere
	
	String id(getId());
	int number = 1;
	int digits = 0;

	if(id.empty())
		id = "Bone";

	// If there is a number
	// already at the end of the
	// id, then remove it.
	if(id[id.size() - 1] <= '9' && id[id.size() - 1] >= '0')
	{
		// figure out how many digits it is
		for (digits = 0; (int)id.size() - 1 >= digits && id[id.size() - 1 - digits] <= '9' && id[id.size() - 1 - digits] >= '0'; digits++);

		String str_number;
		str_number = String(id, id.size() - digits, id.size());
		id = String(id, 0, id.size() - digits);

		number = atoi(str_number.c_str());
	}
	else
	{
		number = 1;
		digits = 3;
	}

	number++;

	// Add the number back onto the id
	{
		const String format(strprintf("%%0%dd", digits));
		id += strprintf(format.c_str(), number);
	}

	// Set the ID
	setId(id);
}

void ARTStateBoneContext::refresh_tool_options()
{
	// IAKKO: Non completamente chiaro
	
	dbg("void ARTStateBoneContext::refresh_tool_options()");
	
	App::dialog_tool_options->clear();
	App::dialog_tool_options->set_widget(options_table);
	App::dialog_tool_options->set_local_name(_("Bone Tool"));
	App::dialog_tool_options->set_name("bone");

	// Button che consente all'utente di "chiudere" il bone corrente

	App::dialog_tool_options->add_button(
		Gtk::StockID("gtk-add"),
		_("Make Bone")
	)->signal_clicked().connect(
		sigc::hide_return(sigc::mem_fun(
			*this,
			&ARTStateBoneContext::run
		)) );
}

void ARTStateBoneContext::run()
{
	dbg("void ARTStateBoneContext::run()");

	//make_bone( point_holder, p2 );
	doBone();

	// Che fare per ripulire i ducks?
	//reset(); // no
	//get_work_area()->clear_ducks();	// nemmeno...
	//get_canvas_interface()->get_selection_manager()->clear_selected_layers(); // neppure
}

Smach::event_result ARTStateBoneContext::event_refresh_tool_options(const Smach::event& /*x*/)
{
	// IAKKO: Non completamente chiaro
	dbg("Smach::event_result ARTStateBoneContext::event_refresh_tool_options(const Smach::event& /*x*/)");
	refresh_tool_options();
	return Smach::RESULT_ACCEPT;
}

Smach::event_result ARTStateBoneContext::event_stop_handler(const Smach::event& /*x*/)
{
	// IAKKO: Non completamente chiaro
	dbg("Smach::event_result ARTStateBoneContext::event_stop_handler(const Smach::event& /*x*/)");
	throw Smach::egress_exception();
}

Smach::event_result ARTStateBoneContext::event_refresh_handler(const Smach::event& /*x*/)
{
	// IAKKO: Non completamente chiaro
	dbg("Smach::event_result ARTStateBoneContext::event_refresh_handler(const Smach::event& /*x*/)");
	refresh_ducks();
	return Smach::RESULT_ACCEPT;
}

Smach::event_result ARTStateBoneContext::event_layer_selection_changed_handler(const  Smach::event & x)
{
	dbg("Smach::event_result ARTStateBoneContext::event_result event_layer_selection_changed_handler(const  Smach::event & x)");
	
	if(egress_on_selection_change)
		throw Smach::egress_exception();
	return Smach::RESULT_OK;
}

void ARTStateBoneContext::make_bone(const Point& _p1, const Point& _p2)
{
	// IAKKO: metodo ausiliario di generazione dell'immagine riguardante l'oggetto
	dbg("void ARTStateBoneContext::make_bone(const Point& _p1, const Point& _p2)");
}

void ARTStateBoneContext::doPutPoint(Point p_rPoint)
{
	m_rPoints.push_back(p_rPoint);
	refresh_ducks();
}

void ARTStateBoneContext::doBone()
{
	dbg("doBone() %s", getId().c_str());
	
	synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("New Bone"));
	synfigapp::PushMode push_mode(get_canvas_interface(),synfigapp::MODE_NORMAL);

	Layer::Handle layer;

	Canvas::Handle canvas;
	int depth(0);

	// we are temporarily using the layer to hold something
	layer=get_canvas_view()->get_selection_manager()->get_selected_layer();
	if(layer)
	{
		depth=layer->get_depth();
		canvas=layer->get_canvas();
	}

	synfigapp::SelectionManager::LayerList layer_selection;
	if (!getenv("SYNFIG_TOOLS_CLEAR_SELECTION"))
		layer_selection = get_canvas_view()->get_selection_manager()->get_selected_layers();

	if(!canvas)
		canvas = get_canvas_view()->get_canvas();

	//value_node_bline->set_member_canvas(canvas);
	
	// IAKKO: Creo un layer di tipo Polygon
	layer = get_canvas_interface()->add_layer_to("polygon", canvas, depth);
	layer_selection.push_back(layer);
	
	// IAKKO: Imposto il nome del layer (che appare anche nella lista dei layer) e lo connetto
	//        ad un evento in modo tale da aggiornare l'oggetto se cambia
	layer->set_description(getId());
	get_canvas_interface()->signal_layer_new_description()(layer,layer->get_description());
	
	// IAKKO: Imposto la lista di puntI che interessano il poligono
	layer->disconnect_dynamic_param("vector_list");
	layer->set_param("vector_list", m_rPoints);
	
	// IAKKO: Imposto il colore
	layer->set_param("color", synfigapp::Main::get_background_color());
	get_canvas_interface()->signal_layer_param_changed()(layer,"color");
	
	// IAKKO: Incremento l'ID dello strumento per il layer successivo
	increment_id();
}

Smach::event_result ARTStateBoneContext::event_mouse_click_handler(const Smach::event& x)
{
	const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));

	switch(event.button)
	{ // IAKKO: Seleziono la chiave
		case BUTTON_LEFT:
// MESCO: l'if al posto di switch si è reso necessario dopo aver inserito il pezzo sperimentale,
// in quanto l'inizializzazione degli oggetti local genera un jump across case
			if( event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN )
			{
			/*
				etl::handle<Duck> duck = new Duck();

				point_holder=get_work_area()->snap_point_to_grid(event.pos);
				duck->set_point(point_holder);
				duck->set_name("p1");
				duck->set_type(Duck::TYPE_POSITION);
				duck->set_editable(false);
				get_work_area()->add_duck(duck);
				*/

// MESCO: Inizio codice sperimentale
#ifdef MESCOCHECK
				// Create the action group
				synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("New Bone"));
				std::vector<BLinePoint> new_list;

//				ValueNode_Bone::Handle value_node_bone(ValueNode_Bone::create(new_list));
				ValueNode_Bone::Handle value_node_bone;
//				assert(value_node_bone);
		
				ValueNode_Const::Handle value_node_origin(ValueNode_Const::create(Vector()));
				assert(value_node_origin);
				/*
				// Set the looping flag (a che serve?, commento...)
				value_node_bone->set_loop(loop_);
				*/
				// Add the Bone to the canvas
				if(/*get_auto_export_flag() &&*/ !get_canvas_interface()->add_value_node(value_node_bone,getId()))
				{
					//get_canvas_view()->get_ui_interface()->error(_("Unable to add value node"));
					group.cancel();
					increment_id();
					throw String(_("Unable to add value node bone"));
					return Smach::RESULT_ERROR;

#endif
				// Provoca l'incremento dell'ID
				//increment_id();
				return Smach::RESULT_OK;
				// Mi aspetterei che RESULT_ACCEPT è la fine della figura
				// mentre RESULT_OK è uno step intermedio...
				// In realtà nella figura Polygon viene aggiunto un pulsante
				// "Crea Poligono" che provvede a stabilizzare la figura.
				// Potresti aggiungere una toolbar in basso con label "Create Bone"
				// Usa il codice che avevamo nella mod_skeleton

// MESCO: Fine codice esperimentale
			}
			else if( event.key == EVENT_WORKAREA_MOUSE_BUTTON_UP )
			{
				m_rPointStop = get_work_area()->snap_point_to_grid(event.pos); // Saving ending point
				dbg("Stop Clicking: <%.5f, %.5f>", (double)m_rPointStop[0], (double)m_rPointStop[1]);
				
				doPutPoint(get_work_area()->snap_point_to_grid(event.pos));
				
				return Smach::RESULT_ACCEPT;
			}
			else if( event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG ) 
			{
				Point p = get_work_area()->snap_point_to_grid(event.pos) - m_rPointStart; // Saving drag starting point
//<<<<<<< .mine
				get_work_area()->queue_draw();
				printf("[IAKKO]: Dragging: %.5f\n", sqrt(pow((double)p[0], 2.0) + pow((double)p[1], 2.0)));
//=======
				dbg("Dragging: %.5f", sqrt(pow((double)p[0], 2.0) + pow((double)p[1], 2.0)));
//>>>>>>> .r12
				
				return Smach::RESULT_ACCEPT;
			}

		break;
	}
	return Smach::RESULT_OK;
}

#ifdef IAKKO
/* commento momentaneamente la versione di IAKKO */
Smach::event_result ARTStateBoneContext::event_mouse_click_handler(const Smach::event& x)
{
	// IAKKO: Questo metodo viene richiamato nel momento in cui l'utente, dopo aver selezionato l'utensile, clicca
	//        sull'area di lavoro ed effettua il drag. Attraverso la posizione iniziale e finale del mouse e' possibile
	//        far disegnare all'interno del canvas cio' che si vuole.
	
	const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));

	switch(event.button)
	{ // IAKKO: Seleziono la chiave
		case BUTTON_LEFT:
			switch(event.key)
			{ // IAKKO: Seleziono il pulsante
				case EVENT_WORKAREA_MOUSE_BUTTON_DOWN:
					m_rPointStart = get_work_area()->snap_point_to_grid(event.pos); // Saving starting point
					printf("[IAKKO]: Start Clicking: <%.5f, %.5f>\n", (double)m_rPointStart[0], (double)m_rPointStart[1]);
					return Smach::RESULT_ACCEPT;
				break;
				case EVENT_WORKAREA_MOUSE_BUTTON_UP:
					m_rPointStop = get_work_area()->snap_point_to_grid(event.pos); // Saving starting point
					printf("[IAKKO]: Stop Clicking: <%.5f, %.5f>\n", (double)m_rPointStop[0], (double)m_rPointStop[1]);
					
					doPutPoint(get_work_area()->snap_point_to_grid(event.pos));
					
					return Smach::RESULT_ACCEPT;
				break;
				case EVENT_WORKAREA_MOUSE_BUTTON_DRAG:
					Point p = get_work_area()->snap_point_to_grid(event.pos) - m_rPointStart; // Saving starting point
					printf("[IAKKO]: Dragging: %.5f\n", sqrt(pow((double)p[0], 2.0) + pow((double)p[1], 2.0)));
					
					return Smach::RESULT_ACCEPT;
				break;
			}
		break;
	}

	return Smach::RESULT_OK;
	/*
	if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN && event.button == BUTTON_LEFT)
	{
		Point p = get_work_area()->snap_point_to_grid(event.pos);
		printf("[IAKKO]:     point_holder: <%5f, %5f\n", (double)p[0], (double)p[1]);
		return Smach::RESULT_ACCEPT;
	}

	if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button == BUTTON_LEFT)
	{
		Point p = get_work_area()->snap_point_to_grid(event.pos);
		printf("[IAKKO]:     point_holder: <%5f, %5f\n", (double)p[0], (double)p[1]);
		return Smach::RESULT_ACCEPT;
	}
	*/
	
	/*
	if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN && event.button == BUTTON_LEFT)
	{
		point_holder=get_work_area()->snap_point_to_grid(event.pos);
		etl::handle<Duck> duck=new Duck();
		duck->set_point(point_holder);
		duck->set_name("p1");
		duck->set_type(Duck::TYPE_POSITION);
		duck->set_editable(false);
		get_work_area()->add_duck(duck);

		point2_duck=new Duck();
		point2_duck->set_point(Vector(0,0));
		point2_duck->set_name("radius");
		point2_duck->set_origin(duck);
		point2_duck->set_radius(true);
		point2_duck->set_scalar(-1);
		point2_duck->set_type(Duck::TYPE_RADIUS);
		point2_duck->set_hover(true);
		get_work_area()->add_duck(point2_duck);

		return Smach::RESULT_ACCEPT;
	}

	if(event.key==EVENT_WORKAREA_MOUSE_BUTTON_DRAG && event.button==BUTTON_LEFT)
	{
		if (!point2_duck) return Smach::RESULT_OK;
		point2_duck->set_point(point_holder-get_work_area()->snap_point_to_grid(event.pos));
		get_work_area()->queue_draw();
		return Smach::RESULT_ACCEPT;
	}

	if(event.key==EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button==BUTTON_LEFT)
	{
		Point point(get_work_area()->snap_point_to_grid(event.pos));

		if (App::restrict_radius_ducks)
		{
			if ((point[0] - point_holder[0]) < 0) point[0] = point_holder[0];
			if ((point[1] - point_holder[1]) < 0) point[1] = point_holder[1];
		}

		make_bone(point_holder, point);
		get_work_area()->clear_ducks();
		return Smach::RESULT_ACCEPT;
	}
	*/
	
	return Smach::RESULT_OK;
}
#endif // IAKKO

void
ARTStateBoneContext::refresh_ducks()
{
	// IAKKO: Pulisce il canvas dai ducks rimanenti e si appresta a disegnarli nuovamente, se esistono
	dbg("ARTStateBoneContext::refresh_ducks() [%d]", m_rPoints.size());

	get_work_area()->clear_ducks();
	
	if (m_rPoints.empty()) return;
	
	PointList::iterator iter;
	etl::handle<WorkArea::Duck> lastDuck = 0;
	etl::handle<WorkArea::Duck> currentDuck = 0;
	
	for(iter = m_rPoints.begin(); iter != m_rPoints.end(); iter++)
	{
		currentDuck = new WorkArea::Duck(*iter);
		
		currentDuck->set_name(strprintf("%x", &*iter));
		currentDuck->set_type(Duck::TYPE_POSITION);
		currentDuck->set_editable(true);
		
		//duck->signal_edited().connect( sigc::bind(sigc::mem_fun(*this,&studio::ARTStateBoneContext::onBoneDuckChange),iter) );
		
		get_work_area()->add_duck(currentDuck);
		
		if (lastDuck)
		{
			etl::handle<WorkArea::Bezier> bezier(new WorkArea::Bezier());
			bezier->p1 = bezier->c1 = lastDuck;
			bezier->p2 = bezier->c2 = currentDuck;
			get_work_area()->add_bezier(bezier);
		}
		
		lastDuck = currentDuck;
	}
	
	// IAKKO: Ridisegna il canvas
	get_work_area()->queue_draw();
}

bool ARTStateBoneContext::onBoneDuckChange(const synfig::Point &point, std::list<synfig::Point>::iterator iter)
{
	dbg("Signal Emitted!");
	*iter=point;
	return true;
}

// ###########################################################################
// ### State Bone
// ###########################################################################

ARTStateBone::ARTStateBone()
: Smach::state<ARTStateBoneContext>("bone")
{
	// IAKKO: qui vengono linkati gli eventi EVENT_* con i puntatori alle funzioni richiamate quando si manifesta
	//        quel particolare tipo di evento.
	
	insert(event_def(EVENT_LAYER_SELECTION_CHANGED, &ARTStateBoneContext::event_layer_selection_changed_handler));
	insert(event_def(EVENT_STOP, &ARTStateBoneContext::event_stop_handler));
	insert(event_def(EVENT_REFRESH, &ARTStateBoneContext::event_refresh_handler));
	insert(event_def(EVENT_REFRESH_DUCKS, &ARTStateBoneContext::event_refresh_handler));
	insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN, &ARTStateBoneContext::event_mouse_click_handler));
	insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG, &ARTStateBoneContext::event_mouse_click_handler));
	insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP, &ARTStateBoneContext::event_mouse_click_handler));
	insert(event_def(EVENT_REFRESH_TOOL_OPTIONS, &ARTStateBoneContext::event_refresh_tool_options));
}

ARTStateBone::~ARTStateBone()
{
	
}

void ARTStateBoneContext::doGDA()
{
	GdaClient * client;
	GdaConnection *connection;
      
	client = gda_client_new ();
      
	dbg ("GDA -> CONNECTING...");
	
	connection = gda_client_open_connection_from_string ( client, "MySQL", "USER=regix;PASSWORD=xiger;HOST=127.0.0.1;PORT=3306;DB_NAME=regix", "regix", "xiger", GDA_CONNECTION_OPTIONS_READ_ONLY, NULL);

	if (connection != NULL)
	{
		dbg ("GDA -> CONNECTED");
	}
	else
	{
		dbg ("GDA -> NOT CONNECTED");
	}

	GdaCommand * command;
	GList * list = NULL;
	GList * node;
	GdaDataModel * dm = NULL;
	gboolean errors=FALSE;

	command = gda_command_new ("SELECT * FROM User", GDA_COMMAND_TYPE_SQL, GDA_COMMAND_OPTION_STOP_ON_ERRORS);

	dm = gda_connection_execute_select_command (connection, command, NULL, NULL);

	if (dm != NULL)
	{
		int num = gda_data_model_get_n_rows (dm);
		
		dbg("Num Rows: %d", num);
		
		for (unsigned int i = 0; i < num; i++)
		{
			gchar * string = gda_value_stringify( gda_data_model_get_value_at_col_name (dm, "U_Username", i) );
			
//			const GValue * value = gda_data_model_get_value_at_col_name (dm, "U_Username", i);
//			string = gda_value_stringify (value);

			dbg("Value: %s", string);
		}
	}
	else
	{
		errors = true;
	}

	if (errors)
	{
		dbg("Errori nella query");
	}
	else
	{
		dbg("Query eseguita correttamente");
	}

	gda_command_free (command);
      
	g_object_unref (G_OBJECT (client));
}

